package com.wiscamp.ninechapters.problems.domain.services;

import com.wiscamp.ninechapters.common.core.RandomHelper;
import com.wiscamp.ninechapters.problems.domain.ProblemsErrorCodes;
import com.wiscamp.ninechapters.problems.domain.gateway.TagGateway;
import com.wiscamp.ninechapters.problems.domain.models.sets.KnowledgePointRule;
import com.wiscamp.ninechapters.problems.domain.models.sets.RegroupRule;
import com.wiscamp.ninechapters.problems.domain.models.sets.TagRule;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.RegroupRuleRepository;
import com.wiscamp.ninechapters.problems.enums.Actions;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

@Service
public class RegroupRuleService extends BaseDomainService {

    private final RegroupRuleRepository regroupRuleRepository;

    private final ProblemRepository problemRepository;

    private final TagGateway tagGateway;

    private final String CACHE_NAME = "regroupRules";

    public RegroupRuleService(RegroupRuleRepository regroupRuleRepository, ProblemRepository problemRepository, TagGateway tagGateway) {
        this.regroupRuleRepository = regroupRuleRepository;
        this.problemRepository = problemRepository;
        this.tagGateway = tagGateway;
    }

    /**
     * Gets a regroup rule
     *
     * @param ruleId
     * @return
     */
    @Cacheable(value = CACHE_NAME, key = "#ruleId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public RegroupRule getRegroupRule(@Min(0) long ruleId) {
        var rule = regroupRuleRepository.getRegroupRule(ruleId);
        if (Objects.isNull(rule)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.REGROUP_RULE_NOT_FOUND, ruleId);
        }
        return rule;
    }

    @HandleServiceException
    public Collection<Long> getProblemIdsByRule(@Min(0) long ruleId) {
        // validate rule
        var rule = getRegroupRule(ruleId);

        // 标签规则匹配题目编号列表
        Collection<Long> tagRuleProblemIds = new ArrayList<>();
        // 知识点匹配题目编号列表
        Collection<Long> pointRuleProblemIds = new ArrayList<>();
        // 结果题目编号列表
        Collection<Long> problemIds = new ArrayList<>();

        if (rule.isHasKnowledgePointRule()) {
            // 规则知识点关联题Id列表
            pointRuleProblemIds = getProblemIdsByPointRules(rule.getKnowledgePointRules());
        }

        if (rule.isHasTagRule()) {
            // 规则标签关联题Id列表
            tagRuleProblemIds = getProblemIdsByTagRules(rule.getTagRules(), pointRuleProblemIds);
            problemIds = tagRuleProblemIds;
        }

        return problemIds;
    }


    private Collection<Long> getProblemIdsByPointRules(@NotEmpty Collection<KnowledgePointRule> pointRules) {
        var problemIds = new ArrayList<Long>();
        for (var pointRule : pointRules) {
            var pointIds = new ArrayList<Long>();
            pointIds.add(pointRule.getPointId());
            var childPointIds = tagGateway.getChildKnowledgePointIds(pointIds);
            var tempIds = problemRepository.getProblemIdsByPointAndDifficulty(childPointIds, pointRule.getStartDifficulty(), pointRule.getEndDifficulty());
            var ids = RandomHelper.getRandomCollection(tempIds, pointRule.getProblemQuantity());
            problemIds.addAll(ids);
        }
        return problemIds;
    }

    private Collection<Long> getProblemIdsByTagRules(@NotEmpty Collection<TagRule> tagRules, Collection<Long> excludedProblemIds) {
        var problemIds = new ArrayList<Long>();
        for (var tagRule : tagRules) {
            var tagIds = new ArrayList<Long>();
            tagIds.add(tagRule.getTagId());
            var tempIds = problemRepository.getProblemIdsByTagAndDifficulty(tagIds, tagRule.getStartDifficulty(), tagRule.getEndDifficulty());
            tempIds.removeAll(excludedProblemIds);
            var ids = RandomHelper.getRandomCollection(tempIds, tagRule.getProblemQuantity());
            problemIds.addAll(ids);
        }
        return problemIds;
    }

    /*@HandleServiceException
    public RegroupRule getRegroupRuleByCode(@NotBlank String code, @Min(0) long organizationId) {
        return regroupRuleRepository.getRegroupRuleByCode(code, organizationId);
    }*/


    /*@HandleServiceException
    public Collection<RegroupRule> getRegroupRulesByName(@NotBlank String name, @Min(0) long organizationId) {
        return regroupRuleRepository.getRegroupRulesByName(name, organizationId);
    }*/

    /*@HandleServiceException
    public Collection<RegroupRule> getRegroupRulesByOrganization(@Min(0) long organizationId) {
        return regroupRuleRepository.getRegroupRulesByOrganization(organizationId);
    }*/

    /*@HandleServiceException
    public Collection<RegroupRule> getRulesByCreator(@Min(0) long creatorId) {
        return regroupRuleRepository.getRulesByCreator(creatorId);
    }*/

    /**
     * Creates a regroup rule
     *
     * @param rule
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#rule.ruleId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public RegroupRule createRegroupRule(@NotNull RegroupRule rule, @Min(0) long operatorId) {
        var existedRule = regroupRuleRepository.getRegroupRuleByCode(rule.getRuleCode(), rule.getOrganizationId());
        if (Objects.nonNull(existedRule)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.REGROUP_RULE_CODE_EXISTS, rule);
        }
        rule.createBy(operatorId);
        return regroupRuleRepository.saveRegroupRule(rule);
    }

    /**
     * Updates a regroup rule
     *
     * @param rule
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#rule.ruleId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public RegroupRule updateRegroupRule(@NotNull RegroupRule rule, @Min(0) long operatorId) {
        var existedRule = getRegroupRule(rule.getRuleId());
        existedRule = regroupRuleRepository.getRegroupRuleByCode(rule.getRuleCode(), rule.getOrganizationId());
        if (Objects.nonNull(existedRule) && existedRule.getRuleId() != rule.getRuleId()
                && existedRule.getRuleCode().equalsIgnoreCase(rule.getRuleCode())) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.EXAM_CODE_EXISTS, rule);
        }

        for (var pointRule : existedRule.getKnowledgePointRules()) {
            var existPointRules = rule.getKnowledgePointRules().stream().filter(e->e.getPointId() == pointRule.getPointId()).toList();
            if (existPointRules.isEmpty())  {
                pointRule.setAction(Actions.DELETE);
                rule.getKnowledgePointRules().add(pointRule);
            }
        }

        for (var tagRule : existedRule.getTagRules()) {
            var existTagRules = rule.getTagRules().stream().filter(e->e.getTagId() == tagRule.getTagId()).toList();
            if (existTagRules.isEmpty()) {
                tagRule.setAction(Actions.DELETE);
                rule.getTagRules().add(tagRule);
            }
        }

        /*for (var pointRule : rule.getKnowledgePointRules()) {
            var existPointRules = existedRule.getKnowledgePointRules().stream().filter(e->e.getPointId() == pointRule.getPointId()).toList();
            if (existPointRules.isEmpty()) pointRule.setAction(Actions.CREATE);
        }

        for (var tagRule : rule.getTagRules()) {
            var existTagRules = existedRule.getTagRules().stream().filter(e->e.getTagId() == tagRule.getTagId()).toList();
            if (existTagRules.isEmpty()) tagRule.setAction(Actions.CREATE);
        }

        for (var typeRule : rule.getProblemTypeRules()) {
            var existTypeRules = existedRule.getProblemTypeRules().stream().filter(e->e.getProblemTypeId() == typeRule.getProblemTypeId()).toList();
            if (existTypeRules.isEmpty()) typeRule.setAction(Actions.CREATE);
        }*/

        rule.updateBy(operatorId);
        return regroupRuleRepository.saveRegroupRule(rule);
    }

    /**
     * Deletes a regroup rule
     *
     * @param ruleId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#ruleId")
    @HandleServiceException
    public boolean deleteRegroupRule(@Min(0) long ruleId, @Min(0) long operatorId) {
        var rule = getRegroupRule(ruleId);
        if (Objects.isNull(rule)) return false;
        // 记录操作日志
        rule.delete();
        rule.updateBy(operatorId);
        regroupRuleRepository.saveRegroupRule(rule);
        return true;
    }

    /*@HandleServiceException
    public PagedEntity<RegroupRule> getPagedRules(@NotNull Pagination<RegroupRuleQuery> pagination) {
        return regroupRuleRepository.getPagedRules(pagination);
    }*/

    /*@HandleServiceException
    public Collection<RegroupRule> getSpecialPracticeRules(@NotBlank String gradeLevel) {
        return regroupRuleRepository.getSpecialPracticeRules(gradeLevel);
    }*/
}
